/*
 * Copyright (c) 2017-2019 Vered Information Technology Corp.,Ltd. All rights reserved. 蔷薇信息技术有限公司版权所有.
 */
package com.zhangbo.share.aspect;

import cn.hutool.core.annotation.AnnotationUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import com.zhangbo.share.annotation.LogIgnore;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;

/**
 * 控制层（controller）入参及返回值记录
 */
@Slf4j
@Aspect
@Order(-90)
@Component
public class LoggerAspect {

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.GetMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.PostMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.PatchMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.DeleteMapping)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object recordRequestLog(ProceedingJoinPoint joinPoint) throws Throwable {
        if (null != RequestContextHolder.getRequestAttributes()) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                    .getRequest();
            String requestParam = getRequestParams(joinPoint, request);
            log.info("请求信息，url：{}, 入参: {}", request.getRequestURL().toString(), requestParam);
        } else if (null != joinPoint && null != joinPoint.getSignature()) {
            String className = joinPoint.getSignature().getDeclaringType().getName();
            String methodName = joinPoint.getSignature().getName();
            Object[] args = joinPoint.getArgs();
            log.info("请求信息，方法：{}, 入参: {}", className + methodName, args);
        }
        Object result = joinPoint.proceed();
        String resultStr = null;
        if (null != result) {
            LogIgnore ignorAnnotation = AnnotationUtil.getAnnotation(result.getClass(), LogIgnore.class);
            String[] names = null;
            if (null != ignorAnnotation) {
                names = ignorAnnotation.names();
            }

            if (null != names && names.length > 0) {
                PropertyPreFilters filters = new PropertyPreFilters();
                PropertyPreFilters.MySimplePropertyPreFilter exclude = filters.addFilter();
                exclude.addExcludes(names);
                resultStr = JSONObject.toJSONString(result, exclude);
            } else {
                resultStr = JSONObject.toJSONString(result);
            }
        }
        log.info("返回数据：{}", resultStr);
        return result;
    }

    /**
     * 获取请求入参信息，兼容各种请求类型
     *
     * @param point
     * @param request
     * @return
     */
    private String getRequestParams(ProceedingJoinPoint point, HttpServletRequest request) {
        String contentType = request.getContentType();
        if (StringUtils.isNotBlank(contentType) && contentType.toLowerCase().startsWith(MediaType.APPLICATION_JSON_VALUE)) {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Annotation[][] parameterAnnotations = signature.getMethod().getParameterAnnotations();
            for (Annotation[] parameterAnnotation : parameterAnnotations) {
                int paramIndex = ArrayUtils.indexOf(parameterAnnotations, parameterAnnotation);
                for (Annotation annotation : parameterAnnotation) {
                    if (annotation instanceof RequestBody) {
                        LogIgnore ignorAnnotation = AnnotationUtil.getAnnotation(point.getArgs()[paramIndex].getClass(), LogIgnore.class);
                        String[] names = null;
                        if (null != ignorAnnotation) {
                            names = ignorAnnotation.names();
                        }
                        if (null != names && names.length > 0) {
                            PropertyPreFilters filters = new PropertyPreFilters();
                            PropertyPreFilters.MySimplePropertyPreFilter exclude = filters.addFilter();
                            exclude.addExcludes(names);
                            return JSONObject.toJSONString(point.getArgs()[paramIndex], exclude);
                        }
                        return JSONObject.toJSONString(point.getArgs()[paramIndex]);
                    }
                }
            }
        } else {
            return JSONObject.toJSONString(request.getParameterMap());
        }
        return null;
    }


}
